package com.wingsboat.common.protect.aspect;

import cn.hutool.core.util.ArrayUtil;
import com.wingsboat.common.core.utils.JoinPointUtil;
import com.wingsboat.common.core.utils.StringUtils;
import com.wingsboat.common.protect.annotation.AthenaLock;
import com.wingsboat.common.protect.annotation.Idempotent;
import com.wingsboat.common.protect.keyconvert.KeyConvert;
import com.wingsboat.common.protect.lockProcessor.AthenaLockProcessor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.Ordered;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author wangzhong
 * @description: 事务枷锁切面类
 *
 * AOP 的 Around Advise 不会执行 joinPoint，而是会将 joinPoint 交给 AthenaLockProcessor 的实现类，方便对目标方法进行加锁。
 *
 *
 * @date 2024/5/30 16:32
 */
@Aspect
@Component
public class AthenaLockAspect implements Ordered {
    /** 锁处理器集合**/
    private final Map<Class<? extends AthenaLockProcessor>, AthenaLockProcessor> lockProcessors = new ConcurrentHashMap<>();

    /** 锁key转换器集合**/
    private final Map<Class<? extends KeyConvert>, KeyConvert> keyConverts = new ConcurrentHashMap<>();

    private final ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();

    private final ExpressionParser expressionParser = new SpelExpressionParser();
    /**
     * 注入锁处理器
     * @param athenaLockProcessor
     */
    public void addAthenaLockProcessor(AthenaLockProcessor athenaLockProcessor){
        lockProcessors.put(athenaLockProcessor.getClass(),athenaLockProcessor);
    }

    /**
     * 注入锁key转换器
     * @param keyConvert
     */
    public void addKeyConvert(KeyConvert keyConvert){
        keyConverts.put(keyConvert.getClass(),keyConvert);
    }



    @Around("@annotation(athenaLock)")
    public Object around(ProceedingJoinPoint joinPoint,AthenaLock athenaLock) throws Throwable {
        String key = generateKey(joinPoint,athenaLock);
        return lockAndProceed(joinPoint, athenaLock, key);
    }

    /**
     * 处理逻辑类
     * @param joinPoint
     * @param athenaLock
     * @param key
     * @return
     * @throws Throwable
     */
    private Object lockAndProceed(ProceedingJoinPoint joinPoint, AthenaLock athenaLock, String key) throws Throwable {
        AthenaLockProcessor lockProcessor = lockProcessors.get(athenaLock.lockProcessor());
        return lockProcessor.proceed(joinPoint, key, athenaLock.leaseTime());
    }

    /**
     * 生产key prefix + convertKey + spElKey
     * @param joinPoint
     * @param athenaLock
     * @return
     */
    private String generateKey(ProceedingJoinPoint joinPoint,AthenaLock athenaLock) {
        String prefix = athenaLock.prefix();
        String convertKey = parserKeyConvert(joinPoint,athenaLock);
        String spELKey = parserSpEL(joinPoint,athenaLock);
        String key = StringUtils.stringJoin(athenaLock.keySeparator(),prefix,convertKey,spELKey);
        return key;
    }

    /**
     * 获取key
     * @param joinPoint
     * @param athenaLock
     * @return
     */
    private String parserKeyConvert(ProceedingJoinPoint joinPoint,AthenaLock athenaLock) {
        KeyConvert keyConvert = keyConverts.get(athenaLock.keyConvert());
        return keyConvert.getKey(joinPoint,athenaLock);
    }

    /**
     * spEL 解析
     * @param joinPoint
     * @param athenaLock
     * @return
     */
    private String parserSpEL(ProceedingJoinPoint joinPoint, AthenaLock athenaLock) {
        String expressionString = athenaLock.spEl();
        if(StringUtils.isEmpty(expressionString)) return expressionString;

        // 获得被拦截方法参数名列表
        Method method = JoinPointUtil.getMethod(joinPoint);
        Object[] args = joinPoint.getArgs();
        String[] parameterNames = this.parameterNameDiscoverer.getParameterNames(method);
        // 准备 Spring EL 表达式解析的上下文
        StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
        if (ArrayUtil.isNotEmpty(parameterNames)) {
            for (int i = 0; i < parameterNames.length; i++) {
                evaluationContext.setVariable(parameterNames[i], args[i]);
            }
        }

        // 解析参数
        Expression expression = expressionParser.parseExpression(expressionString);
        String value = expression.getValue(evaluationContext, String.class);
        return value;
    }


    /**
     * @Transactional 切面的 Order 为 Ordered.LOWEST_PRECEDENCE，我们需要保证我们的切面在他前面执行，所以我们 Order 定义为 Ordered.LOWEST_PRECEDENCE - 1 就可以了
     * @return
     */
    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE - 1;
    }
}
